﻿using log4net.Core;
using SuperSocket.ClientEngine;
using SuperSocket.ProtoBase;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Flurl.Http;

namespace MSH.LogClient
{
    internal class LogClientDaemon : ILogClientDaemon
    {
        private EasyClient client;
        private Task ReadLogTask;
        private Task WatchSocketTask;
        private Task<bool> ConnectionTask;
        private ManualResetEvent ResetEvent = new ManualResetEvent(false);
        private MshLog4netSocketAppender _appender;
        private BlockingCollection<LiteDbData<LogRequest>> BlockingLogRequest =
            new BlockingCollection<LiteDbData<LogRequest>>(3000);

        #region 必备属性

        private string AppId
        {
            get { return _appender.AppId; }
        }
        private string BeginMark
        {
            get { return _appender.BeginMark; }
        }
        private string EndMark
        {
            get { return _appender.EndMark; }
        }
        private string Secrect
        {
            get { return _appender.Secrect; }
        }
        private string Host
        {
            get { return _appender.ServerHost; }
        }
        private int Port
        {
            get { return int.Parse(_appender.ServerPort); }
        }
        private string Mode
        {
            get { return _appender.Mode; }
        }
        private int MaxThreadCount { get; set; }
        private bool IsConnected
        {
            get
            {
                if (client == null) return false;
                return client.IsConnected;
            }
        }

        #endregion

        public LogClientDaemon(MshLog4netSocketAppender appender)
        {
            this._appender = appender;
        }

        /// <summary>
        /// 开启守护线程
        /// </summary>
        public void Start()
        {
            //Tcp模式需要开启Tcp客户端
            if (Mode.ToString().ToLower().Equals("tcp"))
            {
                //创建socket客户端
                if (client == null)
                    client = CreatClient();

                //连接socket
                Connect(Host, Port);

                //创建Socket连接监控任务
                CreatSocketWatchTask(Host, Port);
            }

            //创建读取任务
            CreatReadTask(Mode.ToString().ToLower().Equals("tcp"));
        }

        /// <summary>
        /// 日志请求加入队列
        /// </summary>
        /// <param name="logRequest"></param>
        public void ToQueue(LiteDbData<LogRequest> logRequest)
        {
            Task.Factory.StartNew(() =>
            {
                this.BlockingLogRequest.Add(logRequest);
            });
        }

        #region 客户端事件

        private void Client_Error(object sender, ErrorEventArgs e)
        {
            ResetEvent.Reset();
        }

        private void Client_Connected(object sender, EventArgs e)
        {
            //继续读取任务
            ResetEvent.Set();
        }

        private void Client_Closed(object sender, EventArgs e)
        {
            //暂停读取日志
            ResetEvent.Reset();
        }

        #endregion

        #region 守护行为

        private EasyClient CreatClient()
        {
            var client = new EasyClient();
            client.Error += Client_Error;
            client.Connected += Client_Connected;
            client.Closed += Client_Closed;
            client.Initialize(new BeiginEndReceiveFilter(), (request) =>
            {
            });
            return client;
        }

        /// <summary>
        /// 创建Socket守护线程
        /// 负责断线重连
        /// </summary>
        private void CreatSocketWatchTask(string ip, int port)
        {
            if (WatchSocketTask == null)
            {
                WatchSocketTask = Task.Run(() =>
                {
                    while (true)
                    {
                        Thread.Sleep(3000);
                        if (IsConnected) continue;
                        Connect(ip, port);
                    }
                });
            }
        }

        /// <summary>
        /// 创建读取任务
        /// 负责读取上传日志
        /// </summary>
        private void CreatReadTask(bool isTcp = true)
        {
            if (ReadLogTask == null)
            {
                ReadLogTask = Task.Run(() =>
                {
                    while (true)
                    {
                        Thread.Sleep(20);
                        //连接成功了才开始读取
                        if (isTcp)
                            ResetEvent.WaitOne();

                        var logRequest = BlockingLogRequest.Take();
                        if (logRequest == null) continue;
                        SendMessage(logRequest);
                    }
                });
            }
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        private void Connect(string ip, int port)
        {
            try
            {
                if (!IsConnected || ConnectionTask == null)
                {
                    ConnectionTask = client?.ConnectAsync(new IPEndPoint(IPAddress.Parse(ip), port));
                }
            }
            catch (Exception ex)
            {
                ConnectionTask = null;
                client = null;
            }
        }

        #endregion

        #region Http或Tcp发送日志

        private void SendMessage(LiteDbData<LogRequest> logRequest)
        {
            try
            {
                if (Mode.ToString().ToLower().Equals("tcp"))
                    SendTcpMessage(logRequest);
                if (Mode.ToString().ToLower().Equals("http"))
                    SendHttpMessage(logRequest);
            }
            catch
            {
                ToQueue(logRequest);
            }
        }

        /// <summary>
        /// 通过Socket发送日志
        /// </summary>
        /// <param name="level">日志级别</param>
        /// <param name="beginMark">Stocket信息头</param>
        /// <param name="endMark">Socket信息尾</param>
        /// <param name="logRequest">日志请求</param>
        private void SendTcpMessage(LiteDbData<LogRequest> logRequest)
        {
            var level = (LogLevel)logRequest.LogLevel;
            var pack = new StringPackageInfo(level.ToString(), logRequest.Obj.ToJson(), null);
            var msg = $"{BeginMark}{pack.ToJson()}{EndMark}";
            client.Send(Encoding.UTF8.GetBytes(msg));
        }

        /// <summary>
        /// 通过Http发送日志
        /// </summary>
        private void SendHttpMessage(LiteDbData<LogRequest> logRequest)
        {
            var level = (LogLevel)logRequest.LogLevel;
            var url = $"http://{Host}:{Port}/api/LogService/{level.ToString()}";
            url.WithHeaders(new
            {
                AppId,
                Secrect
            })
            .PostJsonAsync(logRequest.Obj)
            .ReceiveJson();
        }

        #endregion
    }
}
